/**
 * <p>Title: Class for float-point calculations in J2ME applications (MIDP 1.0/CLDC 1.0 where float or double types are not available)</p>
 * <p>Description: It makes float-point calculations via integer numbers</p>
 * <p>Copyright: Nikolay Klimchuk Copyright (c) 2002-2007</p>
 * <p>Company: UNTEH</p>
 * <p>License: Free for use only for non-commercial purpose</p>
 * <p>If you want to use all or part of this class for commercial applications then take into account these conditions:</p>
 * <p>1. I need a one copy of your product which includes my class with license key and so on</p>
 * <p>2. Please append my copyright information henson.midp.Float (C) by Nikolay Klimchuk on �About� screen of your product</p>
 * <p>3. If you have web site please append link <a href=�http://henson.newmail.ru�>Nikolay Klimchuk</a> on the page with description of your product</p>
 * <p>That's all, thank you!</p>
 * @author Nikolay Klimchuk http://henson.newmail.ru
 * @version 1.02
 */

public class Float
{
  /** ERROR constant */
  final static private Float ERROR=new Float(Long.MAX_VALUE, Long.MAX_VALUE);
  /** Number of itterations in sqrt method, if you want to make calculations
   * more precise set ITNUM=6,7,... or ITMUN=4,3,... to make it faster */
  final static private int ITNUM=5;
  /** Square root from 3 */
  final static public Float SQRT3=new Float(1732050807568877294L, -18L);
  /** The Float value that is closer than any other to pi, the ratio of the circumference of a circle to its diameter */
  final static public Float PI=new Float(3141592653589793238L, -18L);
  /** Zero constant */
  final static public Float ZERO=new Float();
  /** One constant */
  final static public Float ONE=new Float(1L);
  /** Pi/2 constant */
  final static public Float PIdiv2=PI.Div(2L);
  /** Pi/4 constant */
  final static public Float PIdiv4=PIdiv2.Div(2L);
  /** Pi/6 constant */
  final static public Float PIdiv6=PIdiv2.Div(3L);
  /** Pi/12 constant */
  final static public Float PIdiv12=PIdiv6.Div(2L);
  /** Pi*2 constant */
  final static public Float PImul2=PI.Mul(2L);
  
  final static public Float G90= new Float(90);
  final static public Float G180 = new Float(180);
  final static public Float G270 = new Float(270);
  final static public Float G360 = new Float(360);
  /** Mantissa */
  public long m_Val;
  /** Exponent */
  public long m_E;
  /** Limit of value */
  private long maxLimit=Long.MAX_VALUE/100;
  //
  /**
   * Create object with zero inside
   */
  public Float()
  {
    m_Val=m_E=0;
  }
  /**
   * Create object and makes initialization with value
   * @param value long - value
   */
  public Float(long value)
  {
    m_Val=value;
    m_E=0;
  }
  /**
   * Create object and makes initialization both mantissa and exponent
   * @param value long - mantissa
   * @param e long - exponent
   */
  public Float(long value, long e)
  {
    m_Val=value;
    if(m_Val==0)
      m_E=0;
    else
      m_E=e;
  }
  /**
   * Create object and makes initialization by other Float object
   * @param value Float - source object
   */
  public Float(Float value)
  {
    m_Val=value.m_Val;
    if(m_Val==0)
      m_E=0;
    else
      m_E=value.m_E;
  }

   public void change(long value, long e)
  {
    m_Val=value;
    if(m_Val==0)
      m_E=0;
    else
      m_E=e;
  }

   public void change(long value)
  {
    m_Val=value;
    m_E=0;
  }

  public long toFixedPoint()
  {
    boolean done=false;
    long tmpE=m_E;
    long tmpVal=m_Val;
    //
    while(tmpE!=0)
    {
      if((tmpE<=3) && !done){
          tmpVal=tmpVal<<10;
          done=true;
      }
      if(tmpE<0)
      {
        tmpVal/=10;
        tmpE++;
      }
      else
      {
        tmpVal*=10;
        tmpE--;
      }
    }
    return tmpVal;
  }

  /**
  /**
   * Convert Float object to long number
   * @return long - number
   */
  public long toLong()
  {
    long tmpE=m_E;
    long tmpVal=m_Val;
    //
    while(tmpE!=0)
    {
      if(tmpE<0)
      {
        tmpVal/=10;
        tmpE++;
      }
      else
      {
        tmpVal*=10;
        tmpE--;
      }
    }
    return tmpVal;
  }
  /**
   * Check value of current Float object is NaN
   * @return boolean - true-if NaN, false-if not
   */
  public boolean isError()
  {
    return (this.m_Val==ERROR.m_Val && this.m_E==ERROR.m_E);
  }
  /**
   * Append value of argument to value of current Float object and return as new Float object
   * @param value Float - argument
   * @return Float - current+value
   */
  public Float Add(Float value)
  {
    if(value.Equal(ZERO))
      return new Float(this);
    //
    long e1=m_E;
    long e2=value.m_E;
    long v1=m_Val;
    long v2=value.m_Val;
    // E must be equal in both operators
    while (e1 != e2)
    {
      if(e1 > e2)
      {
        if(Math.abs(v1)<maxLimit)
        {
          v1*=10;
          e1--;
        }
        else
        {
          v2/=10;
          e2++;
        }
      }
      else
      if(e1 < e2)
      {
        if(Math.abs(v2)<maxLimit)
        {
          v2*=10;
          e2--;
        }
        else
        {
          v1/=10;
          e1++;
        }
      }
    }
    //
    if( (v1>0 && v2>Long.MAX_VALUE-v1) || (v1<0 && v2<Long.MIN_VALUE-v1) )
    {
      v1/=10; e1++;
      v2/=10; e2++;
    }
    //
    if(v1>0 && v2>Long.MAX_VALUE-v1)
      return new Float(ERROR);
    else
    if(v1<0 && v2<Long.MIN_VALUE-v1)
      return new Float(ERROR);
    //
    return new Float(v1+v2, e1);
  }
  /**
   * Subtract value of argument from value of current Float object and return as new Float object
   * @param value Float - argument
   * @return Float - current-value
   */
  public Float Sub(Float value)
  {
    if(value.Equal(ZERO))
      return new Float(m_Val, m_E);
    return Add(new Float(-value.m_Val, value.m_E));
  }
  /**
   * Divide value of current Float object on argument and return as new Float object
   * @param value Float - argument
   * @return Float - current/value
   */
  public Float Mul(long value)
  {
    return Mul(new Float(value, 0));
  }
  /**
   * Multiply value of current Float object on argument and return as new Float object
   * @param value Float - argument
   * @return Float - current*value
   */
  public Float Mul(Float value)
  {
    if(value.Equal(ZERO) || this.Equal(ZERO))
      return new Float(ZERO);
    if(value.Equal(ONE))
      return new Float(this);
    //
    boolean negative1=(m_Val<0);
    if(negative1) m_Val=-m_Val;
    boolean negative2=(value.m_Val<0);
    if(negative2) value.m_Val=-value.m_Val;
    // Check overflow and underflow
    do
    {
      if(value.m_Val>m_Val)
      {
        if(Long.MAX_VALUE/m_Val<value.m_Val)
        {
          value.m_Val/=10;
          value.m_E++;
        }
        else
          break;
      }
      else
      {
        if(Long.MAX_VALUE/value.m_Val<m_Val)
        {
          m_Val/=10;
          m_E++;
        }
        else
          break;
      }
    } while(true);
    //
    if(negative1) m_Val=-m_Val;
    if(negative2) value.m_Val=-value.m_Val;
    //
    long e=m_E+value.m_E;
    long v=m_Val*value.m_Val;
    return new Float(v, e);
  }
  /**
   * Divide value of current Float object on argument and return as new Float object
   * @param value Float - argument
   * @return Float - current/value
   */
  public Float Div(long value)
  {
    return Div(new Float(value, 0));
  }
  /**
   * Divide value of current Float object on argument and return as new Float object
   * @param value Float - argument
   * @return Float - current/value
   */
  public Float Div(Float value)
  {
    if(value.Equal(ONE))
      return new Float(this);
    //
    long e1=m_E;
    long e2=value.m_E;
    long v2=value.m_Val;
    if(v2==0L)
      return new Float(ERROR);
    long v1=m_Val;
    if(v1==0L)
      return new Float(ZERO);
    //
    long val=0L;
    while(true)
    {
      val+=(v1/v2);
      v1%=v2;
      if(v1==0L || Math.abs(val)>(Long.MAX_VALUE/10L))
        break;
      if(Math.abs(v1)>(Long.MAX_VALUE/10L))
      {
        v2/=10L;
        e2++;
      }
      else
      {
        v1*=10L;
        e1--;
      }
      val*=10L;
    }
    //
    Float f=new Float(val, e1-e2);
    f.RemoveZero();
    return f;
  }
  public void RemoveZero()
  {
    if(m_Val==0)
      return;
    while ( m_Val%10 == 0 )
    {
     m_Val/=10;
     m_E++;
    }
  }
  /**
   * Is value of current Float object greater?
   * @param x Float - argument
   * @return boolean - true-if current value is greater x, false-if not
   */
  public boolean Great(Float x)
  {
    long e1=m_E;
    long e2=x.m_E;
    long v1=m_Val;
    long v2=x.m_Val;
    //
    while (e1 != e2)
    {
      if(e1 > e2)
      {
        if(Math.abs(v1)<maxLimit)
        {
          v1*=10;
          e1--;
        }
        else
        {
          v2/=10;
          e2++;
        }
      }
      else
      if(e1 < e2)
      {
        if(Math.abs(v2)<maxLimit)
        {
          v2*=10;
          e2--;
        }
        else
        {
          v1/=10;
          e1++;
        }
      }
    }
    //
    return v1>v2;
  }
  /**
   * Is value of current Float object less?
   * @param x Float - argument
   * @return boolean - true-if current value is less x, false-if not
   */
  public boolean Less(long x)
  {
    return Less(new Float(x, 0));
  }
  /**
   * Is value of current Float object less?
   * @param x Float - argument
   * @return boolean - true-if current value is less x, false-if not
   */
  public boolean Less(Float x)
  {
    long e1=m_E;
    long e2=x.m_E;
    long v1=m_Val;
    long v2=x.m_Val;
    //
    while (e1 != e2)
  {
    if(e1 > e2)
    {
      if(Math.abs(v1)<maxLimit)
      {
        v1*=10;
        e1--;
      }
      else
      {
        v2/=10;
        e2++;
      }
    }
    else
    if(e1 < e2)
    {
      if(Math.abs(v2)<maxLimit)
      {
        v2*=10;
        e2--;
      }
      else
      {
        v1/=10;
        e1++;
      }
    }
  }
  //
  return v1<v2;
  }
  /**
   * Equal with current Float object?
   * @param x Float - argument
   * @return boolean - true-if equal, false-if not
   */
  public boolean Equal(Float x)
  {
    long e1=m_E;
    long e2=x.m_E;
    long v1=m_Val;
    long v2=x.m_Val;
    //
    if((v1==0 && v2==0) || (v1==v2 && e1==e2))
      return true;
    // Values with exponent differences more than 20 times never could be equal
    /*
    if(Math.abs(e1-e2)>20)
      return false;
    */
    long diff=e1-e2;
    if(diff<-20 || diff>20)
      return false;
    //
    while (e1 != e2)
    {
      if(e1 > e2)
      {
        if(Math.abs(v1)<maxLimit)
        {
          v1*=10;
          e1--;
        }
        else
        {
          v2/=10;
          e2++;
        }
      }
      else
      if(e1 < e2)
      {
        if(Math.abs(v2)<maxLimit)
        {
          v2*=10;
          e2--;
        }
        else
        {
          v1/=10;
          e1++;
        }
      }
    }
    //
    return (v1==v2);
  }
  /**
   * Reverse sign of value in current Float object and return as new Float object
   * @return Float - new Float object
   */
  public Float Neg()
  {
    return new Float(-m_Val, m_E);
  }
  /**
   * Returns the trigonometric sine of an angle. Special cases: If the argument is NaN or an infinity, then the result is NaN. If the argument is zero, then the result is a zero with the same sign as the argument. A result must be within 1 ulp of the correctly rounded result. Results must be semi-monotonic
   * @param x Float - an angle, in radians
   * @return Float - the sine of the argument
   */
  static public Float sin(Float x)
  {
    while( x.Great(PI) )
      x=x.Sub(PImul2);
    while( x.Less(PI.Neg()) )
      x=x.Add(PImul2);
    // x*x*x
    Float m1=x.Mul(x.Mul(x));
    Float q1=m1.Div(6L);
    // x*x*x*x*x
    Float m2=x.Mul(x.Mul(m1));
    Float q2=m2.Div(120L);
    // x*x*x*x*x*x*x
    Float m3=x.Mul(x.Mul(m2));
    Float q3=m3.Div(5040L);
    // x*x*x*x*x*x*x*x*x
    Float m4=x.Mul(x.Mul(m3));
    Float q4=m4.Div(362880L);
    // x*x*x*x*x*x*x*x*x*x*x
    Float m5=x.Mul(x.Mul(m4));
    Float q5=m5.Div(39916800L);
    //
    Float result=x.Sub(q1).Add(q2).Sub(q3).Add(q4).Sub(q5);
    // 1e-6
    if(result.Less(new Float(-999999, -6)))
      return new Float(-1L);
    // 1e-6
    if(result.Great(new Float(999999, -6)))
      return new Float(1L);
    // 5e-4
    if(result.Great(new Float(-5, -4)) && result.Less(new Float(5, -4)))
      return new Float(0L);
    //
    return result;
  }
  /**
   * Returns the trigonometric cosine of an angle. Special cases: If the argument is NaN or an infinity, then the result is NaN. A result must be within 1 ulp of the correctly rounded result. Results must be semi-monotonic
   * @param x Float - an angle, in radians
   * @return Float - the cosine of the argument
   */
  static public Float cos(Float x)
  {
    while( x.Great(PI) )
      x=x.Sub(PImul2);
    while( x.Less(PI.Neg()) )
      x=x.Add(PImul2);
    // x*x
    Float m1=x.Mul(x);
    Float q1=m1.Div(2L);
    // x*x*x*x
    Float m2=m1.Mul(m1);
    Float q2=m2.Div(24L);
    // x*x*x*x*x*x
    Float m3=m1.Mul(m2);
    Float q3=m3.Div(720L);
    // x*x*x*x*x*x*x*x
    Float m4=m2.Mul(m2);
    Float q4=m4.Div(40320L);
    // x*x*x*x*x*x*x*x*x*x
    Float m5=m4.Mul(m1);
    Float q5=m5.Div(3628800L);
    Float result=ONE.Sub(q1).Add(q2).Sub(q3).Add(q4).Sub(q5);
    // 1e-6
    if(result.Less(new Float(-999999, -6)))
      return new Float(-1L);
    // 1e-6
    if(result.Great(new Float(999999, -6)))
      return new Float(1L);
    // 5e-4
    if(result.Great(new Float(-5, -4)) && result.Less(new Float(5, -4)))
      return new Float(0L);
    //
    return result;
  }
  /**
   * Returns the correctly rounded positive square root of a double value. Special cases: If the argument is NaN or less than zero, then the result is NaN. If the argument is positive infinity, then the result is positive infinity. If the argument is positive zero or negative zero, then the result is the same as the argument. Otherwise, the result is the double value closest to the true mathematical square root of the argument value
   * @param x Float - a value
   * @return Float - the positive square root of a. If the argument is NaN or less than zero, the result is NaN
   */
  static public Float sqrt(Float x)
  {
    int sp=0;
    boolean inv=false;
    Float a,b;
    //
    if(x.Less(ZERO))
      return new Float(ERROR);
    if(x.Equal(ZERO))
      return new Float(ZERO);
    if(x.Equal(ONE))
      return new Float(ONE);
    // argument less than 1 : invert it
    if(x.Less(ONE))
    {
      x=ONE.Div(x);
      inv=true;
    }
    //
    long e=x.m_E/2;
    // exponent compensation
    Float tmp=new Float(x.m_Val, x.m_E-e*2);
    // process series of division by 16 until argument is <16
    while(tmp.Great(new Float(16L)))
    {
      sp++;
      tmp=tmp.Div(16L);
    }
    // initial approximation
    a=new Float(2L);
    // Newtonian algorithm
    for(int i=ITNUM; i>0; i--)
    {
      b=tmp.Div(a);
      a=a.Add(b);
      a=a.Div(2L);
    }
    // multiply result by 4 : as much times as divisions by 16 took place
    while(sp>0)
    {
      sp--;
      a=a.Mul(4L);
    }
    // exponent compensation
    a.m_E+=e;
    // invert result for inverted argument
    if(inv)
      a=ONE.Div(a);
    return a;
  }
  /**
   * Returns the trigonometric tangent of an angle. Special cases: If the argument is NaN or an infinity, then the result is NaN. If the argument is zero, then the result is a zero with the same sign as the argument. A result must be within 1 ulp of the correctly rounded result. Results must be semi-monotonic
   * @param x Float - an angle, in radians
   * @return Float - the tangent of the argument
   */
  static public Float tan(Float x)
  {
    Float c=cos(x);
    if(c.Equal(ZERO)) return new Float(ERROR);
    return (sin(x).Div(c));
  }
  /**
   * Returns the arc cosine of an angle, in the range of 0.0 through pi. Special case: If the argument is NaN or its absolute value is greater than 1, then the result is NaN. A result must be within 1 ulp of the correctly rounded result. Results must be semi-monotonic
   * @param x Float - the value whose arc cosine is to be returned
   * @return Float - the arc cosine of the argument
   */
  static public Float acos(Float x)
  {
    Float f=asin(x);
    if(f.isError())
      return f;
    return PIdiv2.Sub(f);
  }
  /**
   * Returns the arc sine of an angle, in the range of -pi/2 through pi/2. Special cases: If the argument is NaN or its absolute value is greater than 1, then the result is NaN. If the argument is zero, then the result is a zero with the same sign as the argument. A result must be within 1 ulp of the correctly rounded result. Results must be semi-monotonic
   * @param x Float - the value whose arc sine is to be returned
   * @return Float - the arc sine of the argument
   */
  static public Float asin(Float x)
  {
    if( x.Less(ONE.Neg()) || x.Great(ONE) ) return new Float(ERROR);
    if( x.Equal(ONE.Neg()) ) return PIdiv2.Neg();
    if( x.Equal(ONE) ) return PIdiv2;
    return atan(x.Div(sqrt(ONE.Sub(x.Mul(x)))));
  }
  /**
   * Returns the arc tangent of an angle, in the range of -pi/2 through pi/2. Special cases: If the argument is NaN, then the result is NaN. If the argument is zero, then the result is a zero with the same sign as the argument. A result must be within 1 ulp of the correctly rounded result. Results must be semi-monotonic
   * @param x Float - the value whose arc tangent is to be returned
   * @return Float - the arc tangent of the argument
   */
  static public Float atan(Float x)
  {
      boolean signChange=false;
      boolean Invert=false;
      int sp=0;
      Float x2, a;
      // check up the sign change
      if(x.Less(ZERO))
      {
          x=x.Neg();
          signChange=true;
      }
      // check up the invertation
      if(x.Great(ONE))
      {
          x=ONE.Div(x);
          Invert=true;
      }
      // process shrinking the domain until x<PI/12
      while(x.Great(PIdiv12))
      {
          sp++;
          a=x.Add(SQRT3);
          a=ONE.Div(a);
          x=x.Mul(SQRT3);
          x=x.Sub(ONE);
          x=x.Mul(a);
      }
      // calculation core
      x2=x.Mul(x);
      a=x2.Add(new Float(14087812, -7));
      a=new Float(55913709, -8).Div(a);
      a=a.Add(new Float(60310579, -8));
      a=a.Sub(x2.Mul(new Float(5160454, -8)));
      a=a.Mul(x);
      // process until sp=0
      while(sp>0)
      {
          a=a.Add(PIdiv6);
          sp--;
      }
      // invertation took place
      if(Invert) a=PIdiv2.Sub(a);
      // sign change took place
      if(signChange) a=a.Neg();
      //
      return a;
  }
  /**
   * Converts rectangular coordinates (x,�y) to polar (r,�theta). This method computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi. Special cases: If either argument is NaN, then the result is NaN. If the first argument is positive zero and the second argument is positive, or the first argument is positive and finite and the second argument is positive infinity, then the result is positive zero. If the first argument is negative zero and the second argument is positive, or the first argument is negative and finite and the second argument is positive infinity, then the result is negative zero. If the first argument is positive zero and the second argument is negative, or the first argument is positive and finite and the second argument is negative infinity, then the result is the double value closest to pi. If the first argument is negative zero and the second argument is negative, or the first argument is negative and finite and the second argument is negative infinity, then the result is the double value closest to -pi. If the first argument is positive and the second argument is positive zero or negative zero, or the first argument is positive infinity and the second argument is finite, then the result is the double value closest to pi/2. If the first argument is negative and the second argument is positive zero or negative zero, or the first argument is negative infinity and the second argument is finite, then the result is the double value closest to -pi/2. If both arguments are positive infinity, then the result is the double value closest to pi/4. If the first argument is positive infinity and the second argument is negative infinity, then the result is the double value closest to 3*pi/4. If the first argument is negative infinity and the second argument is positive infinity, then the result is the double value closest to -pi/4. If both arguments are negative infinity, then the result is the double value closest to -3*pi/4. A result must be within 2 ulps of the correctly rounded result. Results must be semi-monotonic
   * @param y Float - the ordinate coordinate
   * @param x Float - the abscissa coordinate
   * @return Float - the theta component of the point (r,�theta) in polar coordinates that corresponds to the point (x,�y) in Cartesian coordinates
   */
  static public Float atan2(Float y, Float x)
  {
    // if x=y=0
    if(y.Equal(ZERO) && x.Equal(ZERO))
      return new Float(ZERO);
    // if x>0 atan(y/x)
    if(x.Great(ZERO))
      return atan(y.Div(x));
    // if x<0 sign(y)*(pi - atan(|y/x|))
    if(x.Less(ZERO))
    {
      if(y.Less(ZERO))
        return Float.PI.Sub(atan(y.Div(x))).Neg();
      else
        return Float.PI.Sub(atan(y.Div(x).Neg()));
    }
    // if x=0 y!=0 sign(y)*pi/2
    if(y.Less(ZERO))
      return PIdiv2.Neg();
    else
      return new Float(PIdiv2);
  }

  /*
   * Converts an angle measured in degrees to an approximately equivalent angle measured in radians. The conversion from degrees to radians is generally inexact
   * @param x Float - an angle, in degrees
   * @return Float - the measurement of the angle x in radians
   */
  static public Float toRadians(Float x)
  {
    return x.Mul(PI).Div(180L);
  }
  /**
   * Converts an angle measured in radians to an approximately equivalent angle measured in degrees. The conversion from radians to degrees is generally inexact; users should not expect cos(toRadians(90.0)) to exactly equal 0.0
   * @param x Float - an angle, in radians
   * @return Float - the measurement of the angle angrad in degrees
   */
  static public Float toDegrees(Float x)
  {
    return x.Mul(180L).Div(PI);
  }
}
